%    Copyright (C) 2001, 2002 Aladdin Enterprises.  All rights reserved.
% 
% This software is provided AS-IS with no warranty, either express or
% implied.
% 
% This software is distributed under license and may not be copied,
% modified or distributed except as expressly authorized under the terms
% of the license contained in the file LICENSE in this distribution.
% 
% For more information about licensing, please refer to
% http://www.ghostscript.com/licensing/. For information on
% commercial licensing, go to http://www.artifex.com/licensing/ or
% contact Artifex Software, Inc., 101 Lucas Valley Road #110,
% San Rafael, CA  94903, U.S.A., +1(415)492-9861.

% $Id: gs_icc.ps,v 1.10 2003/07/14 19:32:17 ray Exp $
% ICCBased color space method dictionaries.
% This assumes gs_ciecs2.ps has already been processed.

%
% Note that the substitution procedure in this routine will dynamically
% check for support of ICCBased color space. If such support is not
% provided, the alternative color space will be used.
%
% The validation routine in dictionary (cs_validate) performs a more
% extensive validation than is done for other color spaces, because
% .seticcspace does less checking than most other color space setting
% operators.
%


.currentglobal true .setglobal
.cspace_util begin

%
% A dictionary for mapping the number of components of an ICCBased color
% space to the appropriate alternative color space. This is used only
% if an alternative color space is not specifically provided.
%
/icc_comp_map_dict
  mark 1 /DeviceGray 3 /DeviceRGB 4 /DeviceCMYK .dicttomark
def

%
%   <array1>   get_icc_alternative_space   <name | array2>
%
% Get the alternative color space for an ICCBased color space.
%
/get_icc_alternative_space
  {
    1 get dup /Alternate .knownget
      { exch pop }
      { /N get //icc_comp_map_dict exch get }
    ifelse
  }
bind def


colorspacedict
/ICCBased
  mark
    /cs_potential_indexed_base true
    /cs_potential_pattern_base true
    /cs_potential_alternate true
    /cs_potential_icc_alternate false
    /cs_get_ncomps { 1 get /N get } bind

    /cs_get_range
      {
        1 get dup /Range .knownget
          { exch pop }
          { /N get 2 mul //dflt_range_4 exch 0 exch getinterval }
        ifelse
      }
    bind

    /cs_get_default_color { 1 get /N get { 0 } repeat } bind

    %
    % For generating a gray, RGB, or CMYK equivalent color, we will
    % assume that the alternative color space provides reasonable
    % mapping.
    /cs_get_currentgray
      { //get_icc_alternative_space exec //.cs_get_currentgray exec }
    bind
    /cs_get_currentrgb
      { //get_icc_alternative_space exec //.cs_get_currentrgb exec }
    bind
    /cs_get_currentcmyk
      { //get_icc_alternative_space exec //.cs_get_currentcmyk exec }
    bind

    % a lot of validation is done by the cs_validate method
    /cs_validate
      {
        //check_cie_cspace exec
        dup 1 get
        dup /N get
        dup type /integertype ne
          //setcspace_typecheck
        if
        //icc_comp_map_dict exch known not
          //setcspace_rangecheck
        if
        dup /DataSource get
        dup type dup /stringtype ne exch /filetype ne and
          //setcspace_typecheck
        if
        rcheck not
          //setcspace_invalidaccess
        if
        dup /Range .knownget
          {
            //check_array exec
              {
                type dup /integertype ne exch /realtype ne and
                  //setcspace_typecheck
                if
              }
            forall
          }
        if
        /Alternate .knownget
          {
            //.cs_validate exec
            //.cs_potential_icc_alternate exec not
              //setcspace_rangecheck
            if
          }
        if
      }
    bind

    % substitute the Alternate space, if appropriate
    /cs_substitute
      {
        %
        % A design problem in the Ghostscript graphic library color
        % space code prevents an ICCBased color space from having an
        % ICCBased alternative color space. This situation actually
        % arises fairly frequently in PDF, as an ICCBased color space
        % is used as the substitute color for a Device{Gray|RGB|CMYK}
        % color space, which in turn are used as the alternative color
        % space to another (or possibly even the same) ICCBased color
        % space.
        %
        % This situation causes no fundamental problems, as
        % Ghostscript nominally supports ICCBased color spaces, so the
        % Alternate color space is not used. Where this is not true
        % (primarily because the NOCIE option is selected), the code
        % would (except for the design flaw noted above) select the
        % Alternate of the Alternate color space.
        %
        % The code below works around this problem by suprressing
        % color space substitution for alternative color spaces if
        % the substituting space is an ICCBased color space.
        %
        dup //get_icc_alternative_space exec
        //.cs_substitute exec
        2 copy eq
        1 index //.cs_potential_icc_alternate exec not
        or
          { pop pop dup }
          {
            % retain just the new Alternate space
            exch pop

            % build all new structures in local VM
            .currentglobal 3 1 roll //false .setglobal

            % copy the original ICCBased color space array
            1 index dup length array copy

            % copy the ICCBased dictionary
            dup 1 2 copy get dup length dict copy

            % insert the new alterante color space
            dup /Alternate 7 -1 roll put

            % insert the new dictionary into the arra
            put

            % restore the VM mode
            3 -1 roll .setglobal
          }
        ifelse
      }
    bind

    %
    % The current implementation of ICCBased color spaces requires the
    % DataSource to be a file.
    %
    /cs_prepare
      {
        % make DataSource a file
        dup 1 get /DataSource get type /stringtype eq
          {
            % build all new structures in local VM
            .currentglobal exch //false .setglobal

            % check if we need to copy the color space and dictionary
            2 copy eq
              {
                dup length array copy
                dup 1 2 copy get dup length dict copy put
              }
            if

            % fetch DataSource, setting up stack for multiple puts
            dup 1 2 copy get dup /DataSource 2 copy get

            % convert the string into a file
            /ReusableStreamDecode filter

            % put the file into the dictioary, the dictionary into the array
            put put

            % restore the VM mode
            exch .setglobal
          }
        if
      }
    bind

    %
    % Install the current color space.
    %
    % The current Ghostscript color space implementation requires that
    % color spaces that provide a base or alternative color space set
    % that base/alternative color space to be the current color space
    % before attempting to set the original color space. This can cause
    % difficulty if an ICCBased color space is being used as a substitute
    % color space for a device-specific color space, and uses that same
    % device-specific color space as an alternative space. For this
    % reason, a special _setcolorspace_nosub operator is provided.
    %
    /cs_install
      {
        % set the alternative color space to be the current color space
        dup //get_icc_alternative_space exec //_setcolorspace_nosub exec

        % check for native support
        /.seticcspace where
          { pop //false }
          { //true }
        ifelse
        NOCIE or
          //pop_1       % do nothing
          {
            % Acrobat Reader silently ignores errors with ICC profiles
            % and uses the alternate color space -- do the same.
            mark exch 1 get
              { .seticcspace }
            .internalstopped
            cleartomark
          }
        ifelse
      }
    bind

    % for now, the alternative spaces for an ICCBased color space do 
    % not require special preparation
    /cs_prepare_color { dup 1 get /N get //check_num_stack exec pop } bind
    /cs_complete_setcolor //pop_1

  .dicttomark
put

end     % .cspace_util

NOPSICC { (%END PSICC) .skipeof } if
% Now set up ICC profile loading for PostScript %%BeginICCProfile sections.

systemdict begin
/.ProcessICCcomment { % file comment --  file comment
  dup
  (%%BeginICCProfile) anchorsearch {
    pop pop
    DEBUG { (.ProcessICCcomment found %%BeginICCProfile) print flush } if

    % load an ICC profile defined as comments (hex encoded).
    % Ends with %%End at the start of a line. Read the data into
    % a bytestring to allow seeking. This string can be used as a
    % seekable ReusableStreamDecode filter source by the ICC logic.
    %
    % Since .bigstring needs to know the size, we first read an array of
    % strings each 64000 max length.
    %
    % stack: --file-- (%%BeginICCProfile: ...) 
    1 index 0 (%%EndICCProfile) /SubFileDecode filter
    [ { counttomark 1 add index
        64000 string readhexstring
        not { exit } if
      } loop
    ] exch closefile
    0 1 index { length add } forall
    .bigstring
    exch 0 exch {
      % stack: --file-- (%%BeginICCProfile: ...) --bytestring-- cur_index --string--
      2 copy length add 	% calculate next string start point
      3 1 roll 3 index 3 1 roll putinterval
    } forall
    pop				% discard length of bytestring
    % make a seekable -file- out of the bytestring
    mark /AsyncRead true .dicttomark /ReusableStreamDecode filter
    % stack: --file-- (%%BeginICCProfile: ...) --icc_subfile-- 
    /DeviceCMYK setcolorspace
    << /DataSource 3 -1 roll
       /N 4		% Try CMYK first
    >> { .seticcspace } stopped {
      /DeviceRGB setcolorspace
      dup /N 3 put  { .seticcspace } stopped {
        /DeviceGray setcolorspace
        dup /N 1 put { .seticcspace } stopped {	% last choice
          QUIET not { (   *** Unable to load ICC profile from PostScript DSC comments ***) = flush } if
          pop
        } if
      } if
    } if
  } {
    pop % Not interested in this DSC comment
  } ifelse
} bind def

% Merge ProcessICCcomment with existing handler
/.ProcessICCcomment load /exec load
currentuserparams /ProcessDSCComment get 
dup null eq {pop {pop pop}} if /exec load
4 array astore cvx readonly
<< /ProcessDSCComment 3 -1 roll >> setuserparams

end	% systemdict
%END PSICC
.setglobal
